package com.hzw.saas.web.hbox.util;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.service.storage.util.StorageUtil;
import com.hzw.saas.web.hbox.bo.LocChunkBO;
import com.hzw.saas.web.hbox.constants.HBoxConsts;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;

/**
 * @author zzl
 * @since 02/25/2021
 */
public class HBoxUtil {

    /**
     * 判断是否为根目录ID
     *
     * @param folderId 目录ID
     * @return true or false
     */
    public static boolean isRootFolder(String folderId) {
        return Objects.equals(HBoxConsts.ROOT_FOLDER_ID, folderId);
    }

    /**
     * 判断是否为根目录ID
     *
     * @param folderId 目录ID
     */
    public static void assertIsRoot(String folderId) {
        boolean isRoot = isRootFolder(folderId);
        AssertUtil.assertThrow("非根目录: ".concat(folderId), !isRoot);
    }

    public static String parseFolderId(String userId, String folderId) {
        return isRootFolder(folderId) ? StorageUtil.getRootId(userId, HBoxConsts.SPACE_ID) : folderId;
    }

    /**
     * 判断是否为根目录ID
     *
     * @param folderId 目录ID
     */
    public static void assertNotRoot(String folderId) {
        boolean isRoot = isRootFolder(folderId);
        AssertUtil.assertThrow("不可为根目录: ".concat(folderId), isRoot);
    }

    public static String encodeChunkInfo(List<LocChunkBO> chunkList) {
        if (Objects.isNull(chunkList))
            chunkList = new ArrayList<>();
        return Base64.encode(JSONUtil.toJsonStr(chunkList));
    }

    public static List<LocChunkBO> decode(String chunkInfo) {
        if (StrUtil.isBlank(chunkInfo))
            return new ArrayList<>();
        return JSONUtil.toList(Base64.decodeStr(chunkInfo), LocChunkBO.class);
    }

    /**
     * 获取指定ID的分片列表
     */
    public static List<LocChunkBO> filterChunks(List<LocChunkBO> chunkList, List<String> chunkIds) {
        if (CollectionUtil.isEmpty(chunkIds))
            return new ArrayList<>();
        List<LocChunkBO> resultList = new ArrayList<>();
        chunkIds.removeIf(id -> {
            if (StrUtil.isBlank(id))
                return true;
            LocChunkBO result = chunkList.stream()
                .filter(fileChunkBO -> Objects.equals(fileChunkBO.getChunkId(), id)).findAny().orElse(null);
            if (result != null) {
                resultList.add(result);
                return true;
            }
            return false;
        });
        Assert.isTrue(chunkIds.isEmpty(), "以下分片不存在(id): ".concat(JSONUtil.toJsonStr(chunkIds)));
        return resultList;
    }

    /**
     * 校验上传的分片
     * 1. 校验参数完整性
     * 2. 校验分片长度
     * 3. 计算分片摘要，若摘要信息存在，校验摘要信息
     *
     * @param chunkInfo 分片信息与数据
     */
    public static void verifyFileChunk(LocChunkBO chunkInfo, byte[] chunkData) {
        String chunkStr = JSONUtil.toJsonStr(chunkInfo);
        AssertUtil.assertThrow("未找到分片文件: ".concat(chunkStr), Objects.isNull(chunkData));
        // 校验参数完整性
        boolean paramNotFound = chunkInfo.getStartBytes() == null || chunkInfo.getEndBytes() == null;
        AssertUtil.assertThrow("分片信息不完整,缺少起始字节数", paramNotFound);
        // 校验分片长度
        boolean lengthNotMatch = !Objects.equals(chunkInfo.getEndBytes() - chunkInfo.getStartBytes() + 1, (long) (chunkData.length));
        AssertUtil.assertThrow("分片文件长度校验失败" + chunkStr, lengthNotMatch);
        // 计算分片摘要
        String chunkMd5 = DigestUtil.md5Hex(chunkData);
        // 有摘要信息则比对
        boolean digestNotMatch = StrUtil.isNotBlank(chunkInfo.getDigest()) && !Objects.equals(chunkInfo.getDigest(), chunkMd5);
        AssertUtil.assertThrow("分片MD5校验失败" + chunkStr, digestNotMatch);
        // 无则设置分片摘要
        chunkInfo.setDigest(chunkMd5);
    }


}
